//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<box.h>
///	@path	~/src/lib/math/
///	@date	2007/12/19
///	@desc	Presents a box.

#pragma once

#include "xeres.h"

#include "lib/math/vector.h"

namespace xeres {

	/*!
		\class	Box
		\brief	Presents a box, also can be used as axis aligned bounding box.
	*/
	template<
		typename _Ty
	> class Box : public ArithmeticOp< _Ty >
	{
	public:

		/// \ctor
		Box( void )
			: bmin( infinite() )
			, bmax( ninfinite() )
		{
		}

		/// \ctor_copy
		Box( const Box& b )
			: bmin( b.bmin )
			, bmax( b.bmax )
		{
		}

		/// \ctor_copy
		template< typename _TOther > Box( const Box<_TOther>& b )
		{
			bmin = b.get_min();
			bmax = b.get_max();
		}

		/// \ctor_set
		template< typename _TOther > Box( const Vector<_TOther>& p1 , const Vector<_TOther>& p2 )
		{
			bmin = min( p1 , p2 );
			bmax = max( p1 , p2 );
		}

	public:

		// set with 1
		template<typename _TOther> void set( const Vector<_TOther>& p1 )
		{
			bmin = p1;
			bmax = p1;
		}

		// set with 2
		template<typename _TOther > void set( const Vector<_TOther>& p1 , const Vector<_TOther>& p2 )
		{
			bmin = min( p1 , p2 );
			bmax = max( p1 , p2 );
		}

		// set with 3
		template<typename _TOther> void set(
			const Vector<_TOther>& p1 , const Vector<_TOther>& p2 , const Vector<_TOther>& p3 )
		{
			bmin = min( p1 , p2 );
			bmin = min( bmin , p3 );
			bmax = max( p1 , p2 );
			bmax = max( bmax , p3 );
		}

		// set with 4
		template<typename _TOther> void set(
			const Vector<_TOther>& p1 , const Vector<_TOther>& p2 , const Vector<_TOther>& p3 , const Vector<_TOther>& p4 )
		{
			bmin = min( p1 , p2 );
			bmin = min( bmin , p3 );
			bmin = min( bmin , p4 );
			bmax = max( p1 , p2 );
			bmax = max( bmax , p3 );
			bmax = max( bmax , p4 );
		}

		// width ( about x )
		_Ty width( void ) const {
			return bmax.x - bmin.x;
		}

		// height ( about y )
		_Ty height( void ) const {
			return bmax.y - bmin.y;
		}

		// depth ( about z )
		_Ty depth( void ) const {
			return bmax.z - bmin.z;
		}

		// left ( min x )
		_Ty left( void ) const {
			return bmin.x;
		}

		// right ( max x )
		_Ty right( void ) const {
			return bmax.x;
		}

		// top ( min y )
		_Ty top( void ) const {
			return bmin.y;
		}

		// bottom ( max y )
		_Ty bottom( void ) const {
			return bmax.x;
		}

		// front ( min z )
		_Ty front( void ) const {
			return bmin.z;
		}

		// back ( min x )
		_Ty back( void ) const {
			return bmax.z;
		}

		// bound n points
		template<typename _TOther> void bound( const Vector<_TOther> * p , size_t count )
		{
			bmin = *p;
			bmax = *p;
			for( size_t i = 1 ; i < count ; ++i )
			{
				bmin = min( bmin , p[i] );
				bmax = max( bmax , p[i] );
			}
		}

		// split min value
		template< typename _TVal > void split_min( size_t axis , const _TVal& value )
		{
			assert( !is_empty() );
			bmin[axis] = value;
		}

		// split max value
		template< typename _TVal > void split_max( size_t axis , const _TVal& value )
		{
			assert( !is_empty() );
			bmax[axis] = value;
		}

		// extend box
		template< typename _Ts > void extend( const _Ts& p )
		{
			assert( !is_empty() );
			bmin -= p;
			bmax += p;
		}

		// empty box
		void empty( void )
		{
			bmin.single( infinite() );
			bmax.single( ninfinite() );
		}

		// get min
		const Vector<_Ty>& get_min( void ) const
		{
			assert( !is_empty() );
			return bmin;
		}
		// get max
		const Vector<_Ty>& get_max( void ) const
		{
			assert( !is_empty() );
			return bmax;
		}

		// determine if box is not set
		bool is_empty( void ) const
		{
			return ( bmin.x > bmax.x ) || ( bmin.y > bmax.y ) || ( bmin.z > bmax.z );
		}

		// center
		Vector<_Ty> center( void ) const
		{
			assert( !is_empty() );
			return ( bmin + bmax ) * .5f;
		}

		// distance from center to point
		template< typename _TOther > _Ty distance( const Vector<_TOther>& o ) const
		{
			assert( !is_empty() );
			return center().distance( o );
		}

		// union with a box
		void union_box( const Box& b )
		{
			bmin = min( bmin , b.bmin );
			bmax = max( bmax , b.bmax );
		}

		// union with a point
		template< typename _TOther >
		void union_point( const Vector<_TOther>& p )
		{
			bmin = min( bmin , p );
			bmax = max( bmax , p );
		}

		// intersect a box
		template< typename _TOther >
		void set_intersection( const Box<_TOther>& b )
		{
			assert( !is_empty() );
			bmin = max( bmin , b.bmin );
			bmax = min( bmax , b.bmax );
		}

		// union
		friend Box operator | ( const Box& a , const Box& b )
		{
			assert( !is_empty() );
			Vector<_Ty> bmin;
			Vector<_Ty> bmax;
			bmin = min( a.get_min() , b.get_min() );
			bmax = max( a.get_max() , b.get_max() );
			return Box( bmin , bmax );
		}

		// intersection
		friend Box operator & ( const Box& a , const Box& b )
		{
			assert( !is_empty() );
			Vector<_Ty> bmin;
			Vector<_Ty> bmax;
			bmin = max( a.get_min() , b.get_min() );
			bmax = min( a.get_max() , b.get_max() );
			return Box( bmin , bmax );
		}

		// overlap test
		template< typename _TOther > bool is_overlap( const Box<_TOther>& b ) const
		{
			assert( !is_empty() );
			if( ( bmax.x < b.get_min().x ) || ( bmin.x > b.get_max().x ) )
				return false;
			if( ( bmax.y < b.get_min().y ) || ( bmin.y > b.get_max().y ) )
				return false;
			if( ( bmax.z < b.get_min().z ) || ( bmin.z > b.get_max().z ) )
				return false;
			return true;
		}

		// inside test
		template< typename _TOther > bool is_inside( const Vector<_TOther>& p ) const
		{
			assert( !is_empty() );
			if( p[0] < bmin[0] ) return false;
			if( p[0] > bmax[0] ) return false;
			if( p[1] < bmin[1] ) return false;
			if( p[1] > bmax[1] ) return false;
			if( p[2] < bmin[2] ) return false;
			if( p[2] > bmax[2] ) return false;
			return true;
		}

	private:

	//@ data
		Vector<_Ty>		bmin , bmax;
	};

} // namespace xeres
